/*
 * Copyright 2008 Carnegie Mellon University
 * Licensed under the Apache License, Version 2.0 (the "License"); 
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *  
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, 
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied. See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */
package edu.cmu.lti.sepia.task.retrieval.indri;

import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import edu.cmu.lti.sepia.data.resource.ResourceManager;
import edu.cmu.lti.sepia.util.CmdUtil;
import edu.cmu.lti.sepia.util.ExceptionUtil;


/**
 * Mocking lemurproject.indri.QueryEnvironment
 * @author Hideki
 *
 */
public class QueryEnvironment {

  private String indriHome;
	private String runQueryPath;
	private String dumpIndexPath;
	private List<String> indices = new ArrayList<String>();
	
	public QueryEnvironment( String indriHome ) {
	  this.indriHome = indriHome; 
	  setRunQueryPath(indriHome);
	  setDumpIndexPath(indriHome);
	}
	
	public void updateIndriHome( String indriHome ) {
    this.indriHome = indriHome; 
    setRunQueryPath(indriHome);
    setDumpIndexPath(indriHome);
	}
	
	public void addIndex( String index ) {
	  indices.add(index);
	}
	
	private final static Pattern pIndriResult = Pattern.compile("(-[0-9.]+\\t.+?)[\\n\\r]+(.+?)[\\n\\r]+(?=-[0-9.]+|$)", Pattern.DOTALL);
	
	public ScoredExtentResult[] runQuery( String query, int maxDocs ) {
		
		List<String> cmdList = new ArrayList<String>();
		cmdList.add( runQueryPath );
		cmdList.add( "-printDocuments=true" );
		cmdList.add( "-memory=1200m" );
		cmdList.add( "-printQuery=true" );
		cmdList.add( "-count="+maxDocs );
		cmdList.add( "-query="+query ); 
		//cmdList.add( "-query="+""+query+"\"" ); // need to quote query in case it has multiple terms
		
		for ( String index : indices ) {
			cmdList.add( "-index="+index );
		}
		String[] cmdArray = (String[]) cmdList.toArray(new String[cmdList.size()]);
		
		String stdout;
		if ( ResourceManager.debugMode ) {
			try {
				stdout = ResourceManager.readResource("indri-stdout.txt");
			} catch ( Exception e ) {
				stdout = ExceptionUtil.exceptionToString(e);
			}
		} else {
			stdout = CmdUtil.execute(cmdArray, null);
		}
				
		List<ScoredExtentResult> results = new ArrayList<ScoredExtentResult>();
		ScoredExtentResult[] sers;
		try {
			Matcher mIndriResult = pIndriResult.matcher( stdout );
			
			while ( mIndriResult.find() ) {
				String head = mIndriResult.group(1);
				String passage = mIndriResult.group(2);
				String[] elements = head.split("\t");
				double score = Double.parseDouble( elements[0] );
				int internalId = 0;
				String externalId = elements[1];
				
				ScoredExtentResult r = new ScoredExtentResult( score, internalId, externalId );
				r.content = passage;
				r.text = passage;
				
				results.add( r );
			}
			
			sers = (ScoredExtentResult[]) results.toArray(new ScoredExtentResult[results.size()]);
		} catch (Exception e) {
			e.printStackTrace();
			sers = new ScoredExtentResult[1];
			sers[0] = new ScoredExtentResult(0.00, 1, "ABC");
			sers[0].text = "body part"+e.getMessage();
		}
		
		return sers;
	}
	
	public ParsedDocument[] documents( ScoredExtentResult[] sers ) {
		ParsedDocument[] pds = new ParsedDocument[ sers.length ];
		for ( int i=0; i<sers.length; i++ ) {
			if (sers[i]==null) continue;
			pds[i] = new ParsedDocument();
			pds[i].content = sers[i].content; 
			pds[i].text = sers[i].text; 
		}
		
		return pds;
	}
	
	public String getDocumentFromDocno( String field, String docno ) {
    for (int i=0; i<indices.size(); i++) {
      List<String> cmdList1 = new ArrayList<String>();
      cmdList1.add( dumpIndexPath );
      cmdList1.add( indices.get(i) );
      cmdList1.add( "di" );
      cmdList1.add( "docno" );
      cmdList1.add( docno ); 
      
      String[] cmdArray = (String[]) cmdList1.toArray(new String[cmdList1.size()]);
      
      String stdout;
      try {
        stdout = CmdUtil.execute(cmdArray, null);
      } catch ( Exception e ) {
        return ExceptionUtil.exceptionToString(e);
      }

      int docid;
      try {
        docid = Integer.parseInt(stdout.trim());
      } catch (NumberFormatException e) {
        continue;
      }

      List<String> cmdList2 = new ArrayList<String>();
      cmdList2.add( dumpIndexPath );
      cmdList2.add( indices.get(i) );
      cmdList2.add( "dt" );
      cmdList2.add( docid+"" ); 
      
      String[] cmdArray2 = (String[]) cmdList2.toArray(new String[cmdList2.size()]);

      try {
        return CmdUtil.execute(cmdArray2, null);
      } catch ( Exception e ) {
        return ExceptionUtil.exceptionToString(e);
      }
    }
    return null;
	}
	
	public String dumpStat( String index ) {
      List<String> cmdList = new ArrayList<String>();
      cmdList.add( dumpIndexPath );
      cmdList.add( index );
      cmdList.add( "s" );
      
      String[] cmdArray = (String[]) cmdList.toArray(new String[cmdList.size()]);
      
      String stdout;
      try {
        stdout = CmdUtil.execute(cmdArray, null);
      } catch ( Exception e ) {
        stdout = ExceptionUtil.exceptionToString(e);
      }
      return stdout;
	}
	
	public String dumpInfo() {
	  StringBuilder sb = new StringBuilder();
	  sb.append("$INDRI_HOME  = "+indriHome+" (Exists = "+(new File(indriHome).exists()?"yes":"no")+")\n");
	  sb.append("runquery     = "+runQueryPath+" (Exists = "+(new File(runQueryPath).exists()?"yes":"no")+")\n");
	  sb.append("dumpindex    = "+dumpIndexPath+" (Exists = "+(new File(dumpIndexPath).exists()?"yes":"no")+")\n");
	  sb.append("indices.size = "+indices.size()+"\n");
	  for (int i=0; i<indices.size(); i++) {
	    String index = indices.get(i);
	    sb.append("index"+i+"       = "+index+" (Exists = "+(new File(index).exists()?"yes":"no")+")\n");
	  }
	  for (int i=0; i<indices.size(); i++) {
	    String index = indices.get(i);
	    sb.append( "---- "+index+" ----\n" );
	    sb.append(dumpStat(index)+"\n");
	  }
	  return sb.toString();
	}
	
  public String validateIndri(String indriHome, String[] indices) {
    StringBuilder validationReport = new StringBuilder();
    if (indriHome==null) {
      return "ERROR: $INDRI_HOME is null.";
    }
    File indriHomeDir = new File(indriHome);
    if (!indriHomeDir.exists()) {
      validationReport.append( "$INDRI_HOME cannot be found at: " + indriHomeDir.getAbsolutePath()
              + "<br>\n" );
    }
    File runQuery = new File(runQueryPath);
    if (!runQuery.exists()) {
      validationReport.append( "INDRI binary cannot be found at: " + runQuery.getAbsolutePath()
              + "<br>\n" );
    }
    if ( indices == null ) {
      validationReport.append( "Indices are not registred.<br>\n" );
    } else {
      for (String index : indices) {
        File indexFile = new File(index.trim());
        if (!indexFile.exists()) {
          validationReport.append( "INDRI index cannot be found at: " + indexFile.getAbsolutePath()
                  + "<br><!-- Indices: " + Arrays.toString(indices) + " -->\n" );
        }
      }
    }
    return validationReport.toString();
  }
  
  /**
   * Find the runquery executable which is slightly different depending on os/bundle.
   * @param indriHome
   */
  private void setRunQueryPath(String indriHome) {
    String[] candidates = {"runquery","runquery.exe","IndriRunQuery","IndriRunQuery.exe"};
    for ( String candidate : candidates ) {
      String newPath = checkExecCandidate( runQueryPath, candidate );
      if ( newPath.equals(runQueryPath) ) {
        runQueryPath = newPath;
        break;
      }
      runQueryPath = newPath;
    }
  }
  
  /**
   * Find the dumpindex executable which is slightly different depending on os/bundle.
   * @param indriHome
   */
  private void setDumpIndexPath(String indriHome) {
    String[] candidates = {"dumpindex","dumpindex.exe","IndriDumpIndex","IndriDumpIndex.exe"};
    for ( String candidate : candidates ) {
      String newPath = checkExecCandidate( dumpIndexPath, candidate );
      if ( newPath.equals(dumpIndexPath) ) {
        dumpIndexPath = newPath;
        break;
      }
      dumpIndexPath = newPath;
    }
  }

  private String checkExecCandidate(String path, String execCandidate) {
    if ( path!=null && path.length()>0 && new File(path).exists() ) {
      return path;
    } else {
      return new File(indriHome,"bin/"+execCandidate).getAbsolutePath();
    }
  }
  
}
